#include <stdio.h>
#include "mpi.h"
#include <time.h>

const int MASTER_RANK = 0;

int main(int  argc, char **argv )
{
    int rank, size;
    MPI_Comm new_comm;

    MPI_Init( &argc, &argv );
    MPI_Comm_rank( MPI_COMM_WORLD, &rank );
    MPI_Comm_split( MPI_COMM_WORLD, rank == 0, 0, &new_comm );
    if (rank == MASTER_RANK) 
        master_io( MPI_COMM_WORLD, new_comm );
    else
        slave_io( MPI_COMM_WORLD, new_comm, rank );

    MPI_Finalize( );
    return 0;
}



/* This is the master */

int master_io( master_comm, comm )
MPI_Comm comm;
{
    int l1 = 4, l2 = 4, l3 = 4, size, i, j,k, messagerIndex =0;
    int lineToSendLenght = 3+l1+l3;
    int matrice1[l1][l2], matrice2[l2][l3], matriceF[l1][l3], lineToSend[lineToSendLenght], receivedLine[3];
    MPI_Status status;
    MPI_Request request;

    MPI_Comm_size( master_comm, &size );

    srand ( time(0) );

    //Initialiser matrice 1 + la printer
    printf( "Matrice 1 : \n" );
    for (i=0; i<l1; i++) {
        for (j=0; j<l2; j++) {
            matrice1[i][j] = rand()%3;
            printf("%i ",  matrice1[i][j] );
        }
        printf( "\n" );
    }
    printf( "Matrice 2 : \n");
    //initialiser matrice 2 + la printer
    for (i=0; i<l2; i++) {
        for (j=0; j<l3; j++) {
            matrice2[i][j] = rand()%3;
            printf("%i ",  matrice2[i][j] );
        }
        printf( "\n");
    }

    messagerIndex = 1;
    lineToSend[0] = l1;
    for (i=0; i<l1; i++) {
        for(k = 0; k< l1; k++){
            lineToSend[k+3] = matrice1[i][k];
        }
        for (j=0; j<l3; j++) {
            for(k = 0; k< l1; k++){
                lineToSend[k+3+l1] = matrice2[k][j];
            }
            lineToSend[1] = i;
            lineToSend[2] = j;
            printf("Master send something (%i, %i) to %i\n",i,j,messagerIndex); 
            MPI_Isend(lineToSend, lineToSendLenght, MPI_INT, messagerIndex, 0, master_comm, &request);
            
            messagerIndex++;
            if(messagerIndex >= size){
                messagerIndex = 1;
            }
        }
    }
    
    messagerIndex = 1;
    for (i=0; i<l1; i++) {
        for (j=0; j<l3; j++) {
            printf("Master try to receive something (%i, %i) from %i\n",i,j,messagerIndex);
            MPI_Recv( receivedLine, 3, MPI_INT, messagerIndex, 0, master_comm, &status ); 
            matriceF[receivedLine[1]][receivedLine[2]] = receivedLine[0];

            messagerIndex++;
            if(messagerIndex >= size){
                messagerIndex = 1;
            }
        }
    }

    printf( "Matrice finale : \n" );
    for (i=0; i<l1; i++) {
        for (j=0; j<l3; j++) {
            printf("%i ", matriceF[i][j]);
        }
        printf( "\n");
    }

    lineToSend[0] = -1;
    for (i=1; i< size; i++) {
        MPI_Isend(lineToSend, lineToSendLenght, MPI_INT, i, 0, master_comm, &request);
    }

    return 0;
}




/* This is the slave */

int slave_io( master_comm, comm , number)
MPI_Comm comm;
{
    const int FIRST_TABLE_LENGHT = 0;
    int needToStop = 0;
    MPI_Status status;
    int bufferReceived[256];
    int offset;
    int line[256], column[256], response[3];
    int i = 0, j =0;
    MPI_Request request;

    while(!needToStop){
        printf("Slave #%i try to receive data \n",number);
        MPI_Recv( bufferReceived, 256, MPI_INT, MASTER_RANK, 0, master_comm, &status );    
        offset = bufferReceived[FIRST_TABLE_LENGHT];
        printf("Slave #%i received data (%i,%i)\n",number, bufferReceived[1],bufferReceived[2]);

        //printf("Slave #%i received data : ", number);
        //for(i = 0; i < 11; i++){
        //    printf(" %i", bufferReceived[i]);
        //}
        //printf("\n");

        if(offset >= 0 ){
            for(i = 3, j = 0; i <= offset + 2; i++, j++){
                line[j] = bufferReceived[i];
            }
            
            for(i = 3 + offset, j = 0; i <= offset*2 + 2; i++, j++){
                column[j] = bufferReceived[i];
            }
            response[0] = 0;
            response[1] = bufferReceived[1];
            response[2] = bufferReceived[2];
            for(i = 0; i < offset; i++){
                response[0] += line[i] * column[i];
            }

            MPI_Isend(response, 3, MPI_INT, MASTER_RANK, 0, master_comm, &request);
        }
        else
        {
            printf("Slave #%i terminating\n", number);
            needToStop++;
        }
    }
    printf("Slave #%i terminated\n", number);
    return 0;
}
